/** \file NetworkController.cpp
 *  \author Micha Begert
 *  \created  $Sa 22 Sep 15:15:00 pm CEST 2007
 *  \modified $So 22 Sep 15:15:00 pm CEST 2007
 */

#include "NetworkController.hpp"
#include "NetworkMessage.hpp"

//RakNet
#include "RakNetworkFactory.h"
#include "RakPeerInterface.h"

#include "MessageIdentifiers.h"

#include <iostream>
#include <string>

//boost
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <boost/multi_index/identity.hpp>
#include <boost/multi_index/member.hpp>

using namespace network;
using namespace boost;
using namespace boost::multi_index;

//TODO Add: using namespace game;
namespace {
    static RakPeerInterface    *m_peer;
}


// defines a multiply indexed set with indices by id and by name
// in reverse alphabetical order
typedef multi_index_container <
    Client,
    indexed_by<
        ordered_unique< identity<Client::Id> >,
        ordered_unique< identity<int> >
    >
> clientSet;

// constructor
NetworkController::NetworkController()
{ }// constructor

NetworkController& NetworkController::getNetworkController()
{
    static NetworkController    s_net_controller;
    
    return s_net_controller;
}

bool NetworkController::Host(const Address& a, int max_clients)
{
	m_isServer = true;

    //Client list
    clientSet clients;

    SocketDescriptor    sock( a.Port(), 0 );
    m_peer = RakNetworkFactory::GetRakPeerInterface();

    if( !m_peer->Startup( MAX_CLIENTS, 30, &sock, 1 ) )
    {
        return false;
    }


	std::cout << "Starting the server.\n";
	// We need to let the server accept incoming connections from the clients
	m_peer->SetMaximumIncomingConnections(MAX_CLIENTS);
    return true;
} //Host


bool NetworkController::Connect(const Address& a)
{
	m_isServer = false;

    SocketDescriptor    sock;
    m_peer = RakNetworkFactory::GetRakPeerInterface();


    if( !m_peer->Startup( 1, 30, &sock, 1 ) )
    {
        return false;
    }

    std::cout << "Starting the client.\n";
    
    m_peer->Connect( IpToUrl( a.Ip() ).c_str() , a.Port(), 0, 0 );

    return true;
} // Connect


void NetworkController::Shutdown()
{
} // Shutdown

void NetworkController::Kick( Client::Id )
{
} //Kick

void NetworkController::Ban( Client::Id )
{
} //Ban

void NetworkController::Send( /*const*/ core::bitstream& stream, receiver_mask receivers, ReliabilityLevel rlvl )
{
    //TODO Clients according to bitmask

//    m_peer->Send( reinterpret_cast<char*>( stream.get_data() ), stream.size(), MEDIUM_PRIORITY, static_cast<PacketReliability>( rlvl ), 0, *reinterpret_cast<SystemAddress*>( &receivers ), false );
} // Send

void NetworkController::Send(/*const*/ core::bitstream& stream, Client::Id cid, ReliabilityLevel rlvl)
{
    m_peer->Send( reinterpret_cast<char*>( stream.get_data() ), stream.size(), MEDIUM_PRIORITY, static_cast<PacketReliability>( rlvl ), 0, *reinterpret_cast<SystemAddress*>( &cid ), false );
} // Send

void NetworkController::ProcessMessages()
{
    Packet *packet = m_peer->Receive();

    for (int i=0; packet != 0 && i < PACKAGE_THRESHOLD; ++i)
	{
		if (packet)
		{
			switch (packet->data[0])
			{
			case ID_REMOTE_DISCONNECTION_NOTIFICATION:
				std::cout << "Another client has disconnected.\n";
				break;
			case ID_REMOTE_CONNECTION_LOST:
				std::cout << "Another client has lost the connection.\n";
				break;
			case ID_REMOTE_NEW_INCOMING_CONNECTION:
				std::cout << "Another client has connected.\n";
                packet->systemAddress;
                //std::copy(packet->data,,std::back_inserter(tc));
				break;
			case ID_CONNECTION_REQUEST_ACCEPTED:
				std::cout << "Our connection request has been accepted.\n";
				break;
			case ID_NEW_INCOMING_CONNECTION:
				std::cout << "A connection is incoming.\n";
				break;
			case ID_NO_FREE_INCOMING_CONNECTIONS:
				std::cout << "The server is full.\n";
				break;
			case ID_DISCONNECTION_NOTIFICATION:
				if (m_isServer){
					std::cout << "A client has disconnected.\n";
				} else {
					std::cout << "We have been disconnected.\n";
				}
				break;
			case ID_CONNECTION_LOST:
				if (m_isServer){
					std::cout << "A client lost the connection.\n";
				} else {
					std::cout << "Connection lost.\n";
				}
				break;
			default:
                if (packet->data[0] >= ID_NETWORK_MESSAGE_BEGIN && packet->data[0] < ID_GAME_ACTION_BEGIN){
                    //TODO insert specific network code here
                    std::cout << "Networkmessage received!!!!!!!!!!!\n";

                }
                else if (packet->data[0] >= ID_GAME_ACTION_BEGIN){
                    //TODO use ActionDispatcher to dispatch an action with the message
                }
                else
                {
				    std::cout << "An unknown message with identifier " << static_cast<int>(packet->data[0]) << " has been received.\n";
                }
				break;
			}

			m_peer->DeallocatePacket(packet);
       		packet = m_peer->Receive();
		}
	}
} // ProcessMessage

// destructor
NetworkController::~NetworkController()
{
    RakNetworkFactory::DestroyRakPeerInterface(m_peer);
} // destructor